package membershipsrv

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"github.com/siddontang/go/log"
	"interview/models"
	"interview/pkg/common"
	"interview/pkg/global"
	"interview/pkg/redis"
	"interview/pkg/utils"
	"strconv"
	"strings"
	"time"
)

const (
	// activeCodeExpiration 兑换码在Redis中的有效期
	activeCodeExpiration = 24 * time.Hour
	// amountToPoints 充值金额到积分的转化率, 1 RMB = 10 Points
	amountToPoints = 10
)

// GenerateActiveCode 生成指定类型的会员身份兑换码(与用户openID绑定)
// 由于无法提供商家付款服务，需要用户自行扫码付款后联系管理员, 提供微信昵称和转账截图证明付款,
// 用户一旦在小程序界面选择相应会员类型并确认开通, 会向后台发送请求生成兑换码并存储在Redis中,
// 管理员核实过后在系统中通过微信昵称+头像定位到用户OpenID, 然后查询该用户的ActiveCode,
// 管理员再人工将兑换码发送给用户, 用户再进行相关激活操作。
// 如果 membershipType == MembershipTypeVip, 则amount为有效值, 否则为math.MaxInt
func GenerateActiveCode(openID string, membershipType, amount int) error {
	code, err := utils.GenerateRandomCode()
	ctx := context.Background()
	if err != nil {
		log.Error(errors.WithMessage(err, common.CustomError[common.GenerateActiveCodeError]))
		return errors.New(common.CustomError[common.GetActiveCodeError])
	}

	k := strings.Join([]string{redis.UserActiveCodeKeyPrefix, utils.AesEncrypt(openID)}, redis.Joiner)
	err = global.Redis.SetNX(ctx, k, fmt.Sprintf("%s:%d:%d", utils.AesEncrypt(code), membershipType, amount), activeCodeExpiration).Err()
	if err != nil {
		log.Error(errors.WithMessage(err, common.CustomError[common.SetCodeInRedisError]))
		return errors.New(common.CustomError[common.GetActiveCodeError])
	}

	return nil
}

// ActiveMembership 根据兑换码激活会员身份
// 对openID和code进行校验方式：查看redis中是否有key为user:code:{openID}的kv对,
// 获取到value({code}:{type}:{amount})后，取出其中的code检查是否一致,
// 一致，则更新数据库，删除k-v对;
// 不一致，则返回err
func ActiveMembership(openID, code string) (bool, error) {
	ctx := context.Background()
	k := strings.Join([]string{redis.UserActiveCodeKeyPrefix, utils.AesEncrypt(openID)}, redis.Joiner)
	result, err := global.Redis.Get(ctx, k).Result()
	if err != nil {
		log.Error(errors.WithMessage(err, common.CustomError[common.CodeNotFoundMsgError]))
		return false, errors.New(common.CustomError[common.CodeExpireOrInvalidError])
	}

	arr := strings.Split(result, redis.Joiner)
	if utils.AesDecrypt(arr[0]) != code {
		return false, errors.New(common.CustomError[common.CodeMismatchError])
	}
	var membershipType, amount int
	membershipType, err = strconv.Atoi(arr[1])
	if err != nil {
		log.Error(errors.WithMessage(err, "Redis中兑换码value的Type有误!"))
		return false, errors.New(common.CustomError[common.InternalServerError])
	}
	if membershipType == 0 {
		log.Error("Redis中兑换码value的Type有误!")
		return false, errors.New(common.CustomError[common.InternalServerError])
	}
	amount, err = strconv.Atoi(arr[2])
	if err != nil {
		log.Error(errors.WithMessage(err, "Redis中兑换码value的Amount有误!"))
		return false, errors.New(common.CustomError[common.InternalServerError])
	}
	if amount == 0 {
		log.Error("Redis中兑换码value的Amount有误!")
		return false, errors.New(common.CustomError[common.InternalServerError])
	}

	year, month, day := time.Now().Date()
	start := time.Date(year, month, day, 0, 0, 0, 0, time.UTC)
	data := map[string]interface{}{
		"openID":           openID,
		"membershipType":   arr[1],
		"start":            start,
		"activeCode":       code,
		"membershipPoints": amount * amountToPoints,
	}
	if membershipType > common.MembershipTypeVip {
		data["end"], data["duration"] = getEndAndDuration(membershipType, start)
	}

	_, err = models.CreateMembership(data)

	if err != nil {
		log.Error(errors.WithMessage(err, "向数据库中插入会员记录失败"))
		return false, errors.New(common.CustomError[common.RedeemMembershipError])
	}
	err = global.Redis.Del(ctx, k).Err()
	if err != nil {
		log.Error(errors.WithMessage(err, "删除Redis中的兑换码失败"))
		return false, errors.New(common.CustomError[common.RedeemMembershipError])
	}

	return true, nil
}

// GetMembershipListOfAllUsers TODO 查询全部用户的会员资格记录列表，可根据条件(MembershipType/UserOpenID/Status/StartDate)过滤
func GetMembershipListOfAllUsers(req interface{}) ([]*models.Membership, int64, error) {
	return nil, 0, nil
}

// GetMembershipListByUserOpenID TODO 查询指定用户会员资格记录列表，可根据条件(MembershipType/Status/StartDate)过滤
func GetMembershipListByUserOpenID(openID string, req interface{}) ([]*models.Membership, int64, error) {
	return nil, 0, nil
}

// GetLatestMembershipByUserOpenID 查询指定用户的最新一条会员资格记录
func GetLatestMembershipByUserOpenID(openID string) (*models.Membership, error) {
	membership, err := models.FindLatestMembershipByOpenID(utils.AesEncrypt(openID))
	if err != nil {
		log.Error(errors.WithMessage(err, "数据库查询指定用户的最新一条会员资格记录失败"))
		return nil, errors.New(common.CustomError[common.InternalServerError])
	}

	return membership, nil
}

// DisableActiveCode 在Redis中删除activeCode，防止重复使用或误操作
func DisableActiveCode(openID, activeCode string) (bool, error) {
	k := strings.Join([]string{redis.UserActiveCodeKeyPrefix, utils.AesEncrypt(openID)}, redis.Joiner)
	err := global.Redis.Del(context.Background(), k).Err()
	if err != nil {
		log.Error(errors.WithMessage(err, "删除Redis中的兑换码失败"))
		return false, errors.New(common.CustomError[common.RedeemMembershipError])
	}

	return true, nil
}

func getEndAndDuration(membershipType int, start time.Time) (time.Time, time.Duration) {
	var (
		end      time.Time
		duration = common.DurationMap[membershipType]
	)

	end = start.Add(duration)
	return end, duration
}
